package com.ruoyi.flow.flow.activity;

import com.ruoyi.flow.core.exception.BadRequestException;
import com.ruoyi.flow.core.exception.NotFoundException;
import com.ruoyi.flow.flow.form.FormInfo;
import com.ruoyi.flow.flow.workflow.FlowActivity;
import com.ruoyi.flow.flow.workflow.FlowAssignee;
import com.ruoyi.flow.flow.workflow.FlowCondition;
import com.ruoyi.flow.flow.workflow.FlowService;
import com.google.common.collect.Lists;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 流程节点服务接口。
 * <br/>
 * 流程中无论是事件、任务、连线、网关，都属于一个节点Activity对象.
 * <br/>
 * 任何一个Activity对象都是继承于{@code FlowElement}对象
 * Created by fenghuo on 2018/8/28.
 *
 * @see FlowElement
 */
@Service
public class FlowActivityService {
    private static final Logger LOGGER = LoggerFactory.getLogger(FlowActivityService.class);
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ProcessEngineFactoryBean processEngine;

    @Cacheable("flowCache")
    public FlowNode getFlowNode(String definitionId, String activitiId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionId);
        // 当前流程节点ID
        FlowElement currentFlowElement = bpmnModel.getMainProcess().getFlowElement(activitiId);
        if (currentFlowElement == null) {
            Map<String, FlowElement> flowElementMap = bpmnModel.getMainProcess().getFlowElementMap();
            currentFlowElement = flowElementMap.get(activitiId);
        }
        return (FlowNode) currentFlowElement;
    }

    /**
     * 获取开始任务节点
     *
     * @param definitionId
     * @return
     */
    public StartEvent getStartEvent(String definitionId) {
        BpmnModel bpmnModel = getBpmnModelByDefinitionId(definitionId);
        if (bpmnModel != null) {
            return (StartEvent) bpmnModel.getMainProcess().getInitialFlowElement();
        }
        return null;
    }

    /**
     * 获取当前任务所属的BPMN的FLOWNODE节点信息
     *
     * @param taskIdOrDefinitionId 任务ID或者流程定义ID
     * @return
     */
    @Cacheable("flowCache")
    public FlowNode getFlowNode(String taskIdOrDefinitionId) {
        ExecutionEntity executionEntity = FlowService.taskService().getExecutionByTaskId(taskIdOrDefinitionId);
        FlowElement flowElement = null;
        if (executionEntity == null) {//当前节点是第一个节点，说明参数是defintionId
            BpmnModel bpmnModel = getBpmnModelByDefinitionId(taskIdOrDefinitionId);
            if (bpmnModel != null) {
                flowElement = bpmnModel.getMainProcess().getInitialFlowElement();
            }
        } else {
            String activityId = executionEntity.getActivityId();
            String definitionId = executionEntity.getProcessDefinitionId();
            flowElement = getFlowNode(definitionId, activityId);

        }
        return (FlowNode) flowElement;
    }

    /**
     * 根据模型定义获取模型视图对象
     *
     * @param definitionId
     * @return
     */
    public BpmnModel getBpmnModelByDefinitionId(String definitionId) {
        BpmnModel bpmnModel = null;
        try {
            bpmnModel = repositoryService.getBpmnModel(definitionId);
        } catch (Exception e) {
            LOGGER.error("找不到流程定义ID为 {} 对应的流程模型", definitionId);
        }
        return bpmnModel;
    }

    /**
     * 获取当前任务的下一个节点。有可能是事件、任务或者网关
     *
     * @param currentTaskId
     * @return
     */
    public FlowNode getNextNode(String currentTaskId) {
        FlowNode currentFlowNode = getFlowNode(currentTaskId);
        return getNextNode(currentFlowNode);
    }

    public FlowNode getNextNode(FlowNode currentFlowNode) {
        if (currentFlowNode == null) {
            LOGGER.warn("current flow node is null.");
            return null;
        }
        SequenceFlow sequenceFlow = currentFlowNode.getOutgoingFlows().get(0);
        FlowNode nextFlowElement = (FlowNode) sequenceFlow.getTargetFlowElement();
        return nextFlowElement;
    }

    /**
     * 获取某个任务节点后续的所有除了{@code SequenceFlow}和{@code GateWay}之外的节点
     * 递归查找，直到找到某一条线上有一个符合的节点
     *
     * @param currentTaskId
     * @return
     */
    public List<FlowNode> getNextNodes(String currentTaskId) {
        FlowNode currentFlowNode = getFlowNode(currentTaskId);
        List<FlowNode> flowNodes = Lists.newLinkedList();
        List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
        for (SequenceFlow sequenceFlow : sequenceFlows) {
            recuiveGetNextNode(sequenceFlow, flowNodes);
        }
        return flowNodes;
    }

    public void recuiveGetNextNode(FlowElement currentElement, List<FlowNode> results) {
        if (currentElement instanceof Gateway) {
            Gateway gateway = (Gateway) currentElement;
            List<SequenceFlow> sequenceFlows = gateway.getOutgoingFlows();
            for (SequenceFlow sequenceFlow : sequenceFlows) {
                recuiveGetNextNode(sequenceFlow, results);
            }
        } else if (currentElement instanceof SequenceFlow) {
            SequenceFlow sequenceFlow = (SequenceFlow) currentElement;
            FlowElement element = sequenceFlow.getTargetFlowElement();
            recuiveGetNextNode(element, results);
        } else {
            results.add((FlowNode) currentElement);
        }
    }

    /**
     * 查找后续任务中是否有接受任务，用于出发接收任务
     *
     * @param taskId 当前任务ID
     * @return
     */
    public ReceiveTask findNextReceiveTask(String taskId) {
        ReceiveTask receiveTask = null;
        List<FlowNode> nextFlowNodes = FlowService.activityService().getNextNodes(taskId);
        boolean hasReceiveNode = false;
        for (FlowNode flowNode : nextFlowNodes) {
            if (flowNode instanceof ReceiveTask) {
                hasReceiveNode = true;
                receiveTask = (ReceiveTask) flowNode;
                break;
            }
        }
        // 如果后续没有接受任务，则考虑当作当前节点是子流程中的节点，到子流程外面找，看子流程的流出节点是否是接受任务
        if (!hasReceiveNode) {
            FlowNode nextFlowNode = FlowService.activityService().getNextNode(taskId);
            FlowElementsContainer container = nextFlowNode.getParentContainer();
            if (nextFlowNode instanceof ReceiveTask) {
                receiveTask = (ReceiveTask) nextFlowNode;
            } else if (container instanceof SubProcess && nextFlowNode instanceof EndEvent) {
                SubProcess subProcess = (SubProcess) container;
                List<SequenceFlow> flows = subProcess.getOutgoingFlows();
                if (flows != null && flows.size() > 0) {
                    SequenceFlow flow = flows.get(0);
                    FlowElement element = flow.getTargetFlowElement();
                    if (element instanceof ReceiveTask) {
                        receiveTask = (ReceiveTask) element;
                    } else if (element instanceof Gateway) {//如果是网关，再找一层
                        Gateway gateway = (Gateway) element;
                        List<SequenceFlow> gatewayOutgoingFlows = gateway.getOutgoingFlows();
                        if (gatewayOutgoingFlows != null && gatewayOutgoingFlows.size() > 0) {
                            SequenceFlow gatewayOutgoingFlow = gatewayOutgoingFlows.get(0);
                            FlowElement targetFlowElement = gatewayOutgoingFlow.getTargetFlowElement();
                            if (targetFlowElement instanceof ReceiveTask) {
                                receiveTask = (ReceiveTask) targetFlowElement;
                            }
                        }
                    }
                }
            }
        }
        return receiveTask;
    }

    /**
     * 获取当前任务所在节点的下一节点的信息，不包括网关类节点信息
     *
     * @param currentTaskId
     * @return 返回结果包括：下一节点表单信息；下一节点办理用户信息；下一节点信息
     */
    public FlowActivity getNextActivityInfo(String currentTaskId, List<FlowCondition> conditions) {
        FlowActivity activity = new FlowActivity();
        Task nextTask = FlowService.taskService().getNextTask(currentTaskId, conditions);
        return getActivityInfo(nextTask);
    }

    /**
     * 获取节点信息
     *
     * @param task
     * @return
     */
    public FlowActivity getActivityInfo(Task task) {
        if (task == null) {
            LOGGER.error("task is null.");
            return null;
        }
        FlowActivity activity = new FlowActivity();
        FlowAssignee nextTaskAssignee = FlowService.taskService().getNextTaskSelectAssignee(task);
        activity.setAssignee(nextTaskAssignee);
        FormInfo formInfo = new FormInfo(task);
        activity.setForm(formInfo);
        activity.setActivityId(task.getId());
        activity.setActivityName(task.getName());
        activity.setActivityType(task.getClass().getSimpleName());
        MultiInstanceLoopCharacteristics characteristics = task.getLoopCharacteristics();
        if (characteristics != null) {
            String inputDataItem = characteristics.getInputDataItem();
            activity.setMultiInstance(true);
            activity.setMultiInstanceVariableName(inputDataItem);
        }
        return activity;
    }

    /**
     * 根据任务ID获取任务所在节点的信息
     *
     * @param taskId
     * @return
     */
    @Cacheable("flowCache")
    public FlowActivity getActivityInfo(String taskId) {
        FlowNode flowNode = getFlowNode(taskId);
        if (flowNode != null && flowNode instanceof Task) {
            return getActivityInfo((Task) flowNode);
        }
        return null;
    }

    /**
     * 根据任务ID获取当前任务所在节点的多实例信息
     *
     * @param taskId
     * @return 如果返回null则表示不是多实例节点
     */
    public MultiInstanceLoopCharacteristics getMultiInstanceLoopCharacteristics(String taskId) {
        FlowNode flowNode = getFlowNode(taskId);
        if (Objects.nonNull(flowNode) && flowNode instanceof Task) {
            return ((Task) flowNode).getLoopCharacteristics();
        }
        return null;
    }

    /**
     * 根据执行ID获取节点的多实例信息
     *
     * @param executionId
     * @return
     */
    public MultiInstanceLoopCharacteristics getMultiInstanceLoopCharacteristicsByExecution(String executionId) {
        ExecutionEntity executionEntity = FlowService.taskService().getExecutionByExecutionId(executionId);
        if (executionEntity != null) {
            String definitionId = executionEntity.getProcessDefinitionId();
            String activityId = executionEntity.getActivityId();
            FlowNode flowNode = getFlowNode(definitionId, activityId);
            if (Objects.nonNull(flowNode) && flowNode instanceof Task) {
                return ((Task) flowNode).getLoopCharacteristics();
            }
        }
        return null;
    }

    /**
     * 获取流程图像，已执行节点和流程线高亮显示
     *
     * @param pProcessInstanceId
     * @param response
     */
    public void getActivitiProccessImage(String pProcessInstanceId, HttpServletResponse response) {
        InputStream imageStream = null;
        OutputStream outputStream = null;
        try {
            //  获取历史流程实例
            HistoricProcessInstance historicProcessInstance = FlowService.instanceService().getHistoricProcessInstanceByInstanceId(pProcessInstanceId);
            if (historicProcessInstance == null) {
                throw new NotFoundException("获取流程实例ID[" + pProcessInstanceId + "]对应的历史流程实例失败！");
            } else {
                // 获取流程定义
                ProcessDefinitionEntity processDefinition = FlowService.definitionService().getProcessDefinitionByDefinitionId(historicProcessInstance.getProcessDefinitionId());

                // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
                List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(pProcessInstanceId).orderByHistoricActivityInstanceId().asc().list();

                // 已执行的节点ID集合
                List<String> executedActivityIdList = historicActivityInstanceList.stream().map(activityInstance -> activityInstance.getActivityId()).collect(Collectors.toList());

                // 获取BMPM图形信息
                BpmnModel bpmnModel = getBpmnModelByDefinitionId(historicProcessInstance.getProcessDefinitionId());

                // 已执行的线集合
                List<String> flowIds = getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);

                // 获取流程图图像字符流
                ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
                //配置字体
                imageStream = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds, "微软雅黑", "宋体", "黑体", null, 2.0);
                response.setContentType("image/png");
                outputStream = response.getOutputStream();
                IOUtils.copy(imageStream, outputStream);
            }
        } catch (Exception e) {
            throw new BadRequestException("获取流程历史图形发生错误", e);
        } finally {
            IOUtils.closeQuietly(imageStream);
            IOUtils.closeQuietly(outputStream);
        }
    }

    /**
     * 高亮完成流程输出线
     *
     * @param bpmnModel
     * @param processDefinitionEntity
     * @param historicActivityInstances
     * @return
     */
    private List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //24小时制
        List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());

            List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以保存后续开始时间相同的节点
            FlowNode sameActivityImpl1 = null;

            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一个节点
            HistoricActivityInstance activityImp2_;

            for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
                activityImp2_ = historicActivityInstances.get(k);// 后续第1个节点

                if (activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
                        df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))) //都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                {

                } else {
                    sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());//找到紧跟在后面的一个节点
                    break;
                }

            }
            sameStartTimeNodes.add(sameActivityImpl1); // 将后面第一个节点放在时间相同节点的集合里
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点

                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {// 如果第一个节点和第二个节点开始时间相同保存
                    FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {// 有不相同跳出循环
                    break;
                }
            }
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows(); // 取出节点的所有出去的线

            for (SequenceFlow pvmTransition : pvmTransitions) {// 对所有的线进行遍历
                FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }

        }
        return highFlows;
    }
}
